/**
 * \file parse.cpp
 * \brief Implementation of ATerm to Code parsing
 * \author Wouter Caarls <w.caarls@tnw.tudelft.nl>
 *
 * \verbatim
 * Copyright (c) 2005 Wouter Caarls, Delft University of Technology
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * \endverbatim
 */

#include <sys/types.h>
#include <sys/stat.h>

#include <iostream>
#include <fstream>

#include <string.h>
#include <vector>

#include <unistd.h>
#include <assert.h>
#include <aterm2.h>

#include "Type.h"
#include "Code.h"
#include "Value.h"
#include "Expression.h"
#include "BinaryOp.h"
#include "LeftExpression.h"
#include "Declaration.h"
#include "Symbol.h"

#include "parse.h"

#define PATH_MAX 512

ATerm bottomOfStack;
std::vector<stratego_def_t> strategoDefs;
static Code *last;

void compileStrategies(std::vector<stratego_def_t> defs);

#define _assert(a) do {if (!(a)) parseError(__FILE__, __LINE__, __PRETTY_FUNCTION__, #a);} while (0)

void parseError(const char *file, int line, const char *func, const char *error)
{
  fprintf(stderr, "Parse error after %s%s:%d: %s: Assertion `%s' failed.\n", last->toString().c_str(), file, line, func, error);
  exit(1);  
}

bool isStrategoDef(std::string name)
{
  if (strategoDefs.empty())
    return false;

  for (stratego_def_t::DI ii = strategoDefs.begin(); ii != strategoDefs.end(); ++ii)
    if (name == ii->name)
      return true;

  return false;
}

Code *parseCFile(const char *name)
{
  FILE *f;

  plog.parse() << HDR << "Parsing " << name << std::endl;

  if (!strcmp(name, "-"))
  {
    int filedes[2], r;
    char buf[BUFSIZ];

    popen2a("parse-p", CStr("-i ") << name, filedes);

    while ((r=read(STDIN_FILENO, buf, BUFSIZ)))
      write(filedes[1], buf, r);
    close(filedes[1]);

    f = fdopen(filedes[0], "r");
  }
  else
    f = popen(CStr("parse-p -i ") << name, "r");
  
  ATerm parsed = ATreadFromFile(f);
  fclose(f);

  if (parsed)
    return parseATerm(parsed);
  else
    return NULL;
}

Code *parseC(const char *str)
{
  int filedes[2];

  popen2("parse-p", filedes);

  write(filedes[1], str, strlen(str));
  close(filedes[1]);

  FILE *f = fdopen(filedes[0], "r");
  ATerm parsed = ATreadFromFile(f);
  fclose(f);  

  if (parsed)
    return parseATerm(parsed);
  else
    return NULL;
}

Code *parseFile(int argc, char **argv, char *filename)
{
  static bool first=true;
  Code *parsed;

  if (first)
  {
    // Init aterm library
    ATinit(argc, argv, &bottomOfStack);

    first = false;
  }

  if (filename)
    parsed = parseCFile(filename);
  else
    parsed = parseCFile("-");

  if (!parsed)
  {
    plog.err() << HDR << "couldn't read ATerm from '" << filename << "'" << std::endl;
    return new VoidValue();
  }

  if (strategoDefs.size())
    compileStrategies(strategoDefs);

  return parsed;
}

ATerm ATfilter(ATerm term, char *strategy, char *args)
{
  int filedes[2];
  FILE *fromf, *tof;
  ATerm res;

  if (popen2a(strategy, args, filedes))
  {
    plog.err() << HDR << "could not execute strategy " << strategy << std::endl;
    exit(1);
  }

  fromf = fdopen(filedes[0], "r");
  tof = fdopen(filedes[1], "w");

  ATwriteToBinaryFile(term, tof);
  fclose(tof);
  res = ATreadFromFile(fromf);
  fclose(fromf);

  return res;
}

void compileStrategies(std::vector<stratego_def_t> defs)
{
  CStr file;
  ATermList funcs = ATmakeList0();
  int filedes[2];

  file << options.strategydir << "/" << options.strategyfile;

  plog.stratego() << HDR << "Creating stratego source module " << options.strategyfile << std::endl;

  // Make a backup
  rename(CStr(file) << "-strategies.str", CStr(file) << "-strategies.str~");

  // Open on-line strategies main file
  std::ofstream out(CStr(file) << ".str");

  // Write out stratego preamble
  out << CStr("module ") << options.strategyfile << std::endl
      << "  imports libstratego-lib P" << std::endl
      << "  imports " << options.strategyfile << "-strategies" << std::endl
      << "  strategies" << std::endl
      << "    extraopts =" << std::endl
      << "      ArgOption(\"-s\" + \"--strategy\"" << std::endl
      << "    , where(<set-config> (\"-s\",<id>))" << std::endl
      << "    , !\"-s s|--strategy s   Execute strategy s\"" << std::endl
      << "    )" << std::endl
      << std::endl
      << "    main =" << std::endl
      << "      io-wrap(extraopts, choose)" << std::endl
      << std::endl
      << "    choose =" << std::endl
      << "      ?i; (TupleToList <+ MkSingleton); ?args;" << std::endl
      << "      <table-create>(\"pepci-global\"); " << std::endl
      << "      <table-put>(\"pepci-global\", \"arguments\", args); !i;" << std::endl;

  // Write out argument handling
  for (stratego_def_t::DI ii = strategoDefs.begin(); ii != strategoDefs.end(); ++ii)
  {
    out << "      (where(<get-config> \"-s\" => \"" << ii->name << "\"); " << ii->name
	<< "-strategy)";

    if (ii + 1 != strategoDefs.end())
      out << " +";
    out << std::endl;
  }

  out.close();

  // Construct online strategies
  for (stratego_def_t::DI ii = strategoDefs.begin(); ii != strategoDefs.end(); ++ii)
  {
    plog.stratego() << HDR << "Assembling strategy '" << CStr(ii->name) << "'" << std::endl;

    ATerm func = ATfilter((ATerm)ii->def, "meta-explode", "");
    ATerm args = ATmake("<appl(<str>, <term>)>", "", (char*)(CStr(ii->name) << "-strategy"), func);

    /* Replace main strategy by the strategy name */
    func = ATfilter(args, CStr(options.strategydir) << "/replace-stratego-main", "");

    /* Replace arguments */
    int jjn = 1;
    for (stratego_def_t::PI jj = ii->params.begin(); jj != ii->params.end(); ++jj, ++jjn)
    {
      args = ATmake("<appl(<str>, <int>, <term>)>", "", jj->c_str(), jjn, func);

      /* Replace argument by the appropriate table lookup */
      func = ATfilter(args, CStr(options.strategydir) << "/replace-stratego-argument", "");
    }

    if (ATgetType(func) == AT_LIST)
      funcs = ATconcat(funcs, (ATermList)func);
    else if (ATgetType(func) == AT_APPL)
      funcs = ATinsert(funcs, func);
    else
    {
      plog.err() << HDR << "Failed to assemble strategy '" << CStr(ii->name) << "'"
                 << std::endl;
      exit(1);
    }
  }

  ATerm module = ATmake("<appl(<str>, <term>)>", "Module",
                        (CStr(options.strategyfile) << "-strategies").str().c_str(), funcs);

  // Fire up the pretty printer
  if (popen2a("pp-stratego", "-a", filedes))
  {
    plog.err() << HDR << "could not execute stratego pretty-printer" << std::endl;
    exit(1);
  }

  FILE *tof = fdopen(filedes[1], "w");
  ATwriteToBinaryFile((ATerm)module, tof);
  fclose(tof);

  /* Read result, and write to online strategies file */
  std::ofstream sout(CStr(file) << "-strategies.str");
  char buf[PATH_MAX+1];
  int count;

  while ((count = read(filedes[0], buf, PATH_MAX)))
  {
    buf[count] = '\0';
    sout << buf;
  }
  close(filedes[0]);
  sout.close();

  // Make meta file
  std::ofstream mout(CStr(file) << "-strategies.meta");
  mout << "Meta([Syntax(\"Stratego-P\")])" << std::endl;
  mout.close();

  // See if we need to compile
  if (!fileCompare(CStr(file) << "-strategies.str", CStr(file) << "-strategies.str~") &&
      fileDateCompare(file, CStr(file) << "-strategies.str~") > 0)
  {
    plog.debug() << HDR << "Skipping stratego source compilation" << std::endl;
    rename(CStr(file) << "-strategies.str~", CStr(file) << "-strategies.str");
    return;
  }

  plog.stratego() << HDR << "Compiling stratego source" << std::endl;

  // Compile
  int status = system(CStr("strc -Xcc -m32 -Xlinker -m32 -i ") << (char*)file << ".str"
		      << " -o " << (char*)file
		      << " -I $SC_TOP/share/sdf/p-tools"
		      << " -I " << options.strategydir << "/include -S"
                      << " -la stratego-lib");

  if (!WIFEXITED(status) || WEXITSTATUS(status))
  {
    plog.err() << HDR << "could not execute stratego compiler" << std::endl;
    exit(1);
  }
}

Code *parseATerm(ATerm term)
{
  if (ATgetType(term) != AT_APPL)
  {
    std::cout << "Unable to parse" << std::endl;
    ATerror("Unknown term %n", term);
  }
  else
  {
    ATermAppl appl = (ATermAppl) term;
    AFun fun = ATgetAFun(appl);
    char *funname=ATgetName(fun);
    char *name=NULL;

    if (plog.parse.IsEnabled())
      plog.parse() << HDR << funname << std::endl;

    if (!ATisEmpty(ATgetArguments(appl)) && ATgetType(ATgetArgument(appl, 0)) == AT_APPL)
      name = ATgetName(ATgetAFun((ATermAppl)ATgetArgument(appl, 0)));

    if (!strcmp(funname, "Id"))
    {      
      return new Id(&Unknown, name);
    }

    if (!strcmp(funname, "Field"))
    {
      LeftExpression *data =
	dynamic_cast<LeftExpression*>(parseATerm(ATgetArgument(appl, 0)));
      _assert(data != NULL);

      Code *member = dynamic_cast<Code*>(parseATerm(ATgetArgument(appl, 1)));
      _assert(member != NULL);

      return new Field(&Unknown, data, member);
    } 

    if (!strcmp(funname, "ArrayIndex"))
    {
      LeftExpression *data =
	dynamic_cast<LeftExpression*>(parseATerm(ATgetArgument(appl, 0)));
      _assert(data != NULL);

      Expression *index = dynamic_cast<Expression*>(parseATerm(ATgetArgument(appl, 1)));
      _assert(index != NULL);

      return new ArrayIndex(&Unknown, data, index);
    } 

    if (!strcmp(funname, "IntConst"))
    {
      return
	new IntegerValue(atoi(ATgetName(ATgetAFun((ATermAppl)ATgetArgument(appl, 0)))));
    }

    if (!strcmp(funname, "CharConst"))
    {
      return
	new IntegerValue((ATgetName(ATgetAFun((ATermAppl)ATgetArgument(appl, 0))))[1]);
    }

    if (!strcmp(funname, "Assign"))
    {
      AssignOp op;
      char *opName = ATgetName(ATgetAFun((ATermAppl)ATgetArgument(appl, 1)));

      if (!strcmp(opName, "AssignEq"))
	op = AssignEq;
      else if (!strcmp(opName, "AssignMul"))
	op = AssignMul;
      else if (!strcmp(opName, "AssignDiv"))
	op = AssignDiv;
      else if (!strcmp(opName, "AssignMod"))
	op = AssignMod;
      else if (!strcmp(opName, "AssignPlus"))
	op = AssignPlus;
      else if (!strcmp(opName, "AssignMin"))
	op = AssignMin;
      else if (!strcmp(opName, "AssignSL"))
	op = AssignSL;
      else if (!strcmp(opName, "AssignSR"))
	op = AssignSR;
      else if (!strcmp(opName, "AssignAnd"))
	op = AssignAnd;
      else if (!strcmp(opName, "AssignExOr"))
	op = AssignExOr;
      else if (!strcmp(opName, "AssignOr"))
	op = AssignOr;
      else
      {
        plog.err() << HDR << "Unknown assignment operator '" << opName << "'" << std::endl;
        exit(1);
      }

      LeftExpression *left =
	dynamic_cast<LeftExpression*>(parseATerm(ATgetArgument(appl, 0)));
      _assert(left != NULL);

      Expression *right =
	dynamic_cast<Expression*>(parseATerm(ATgetArgument(appl, 2))); 
      _assert(right != NULL);

      return new Assign(&Unknown, op, left, right);
    } 

    if (!strcmp(funname, "Stat"))
    {
      return last = new Stat(dynamic_cast<Expression*>(parseATerm(ATgetArgument(appl, 0))));
    } 

    if (!strcmp(funname, "FloatConst"))
    {
      return
	new FloatValue(atof(ATgetName(ATgetAFun((ATermAppl)ATgetArgument(appl, 0)))));
    }
      
    if (!strcmp(funname, "CodeConst"))
    {
      return
	new CodeValue(parseATerm(ATgetArgument(appl, 0)));
    }

    if (!strcmp(funname, "EmptyExp"))
    {      
      return new VoidValue();
    }

    if (!strcmp(funname, "Address"))
    {
      LeftExpression *data =
	dynamic_cast<LeftExpression*>(parseATerm(ATgetArgument(appl, 0)));
      _assert(data != NULL);

      return new Address(&Unknown, data);
    } 

    if (!strcmp(funname, "TypeCast"))
    {
      TypeName *tname =
	dynamic_cast<TypeName*>(parseATerm(ATgetArgument(appl, 0)));
      _assert(tname != NULL);
      
      Expression *data =
	dynamic_cast<Expression*>(parseATerm(ATgetArgument(appl, 1)));
      _assert(data != NULL);

      return new TypeCast(&Unknown, tname, data);
    } 

    if (!strcmp(funname, "SizeofExp"))
    {
      Expression *exp =
	dynamic_cast<Expression*>(parseATerm(ATgetArgument(appl, 0)));
      _assert(exp != NULL);
      
      return new SizeofExp(&BasicInteger, exp);
    } 

    if (!strcmp(funname, "SizeofType"))
    {
      TypeName *tname =
	dynamic_cast<TypeName*>(parseATerm(ATgetArgument(appl, 0)));
      _assert(tname != NULL);
      
      return new SizeofType(&BasicInteger, tname);
    } 

    if (!strcmp(funname, "Deref"))
    {
      Expression *data =
	dynamic_cast<Expression*>(parseATerm(ATgetArgument(appl, 0)));
      _assert(data != NULL);

      return new Deref(&Unknown, data);
    } 

    if (!strcmp(funname, "Decode"))
    {
      Expression *data =
	dynamic_cast<Expression*>(parseATerm(ATgetArgument(appl, 0)));
      _assert(data != NULL);

      return new Decode(&Unknown, data);
    } 

    if (!strcmp(funname, "Escape"))
    {
      Expression *data =
	dynamic_cast<Expression*>(parseATerm(ATgetArgument(appl, 0)));
      _assert(data != NULL);

      return new Escape(&Unknown, data);
    } 

    if (!strcmp(funname, "DerefMember"))
    {
      Expression *data =
	dynamic_cast<Expression*>(parseATerm(ATgetArgument(appl, 0)));
      _assert(data != NULL);

      Code *member = dynamic_cast<Code*>(parseATerm(ATgetArgument(appl, 1)));
      _assert(member != NULL);

      return new Field(&Unknown, new Deref(&Unknown, data), member);
    } 

    if (!strcmp(funname, "XTCArrayIndex"))
    {
      LeftExpression *data =
	dynamic_cast<LeftExpression*>(parseATerm(ATgetArgument(appl, 0)));
      _assert(data != NULL);

      Expression *lindex = dynamic_cast<Expression*>(parseATerm(ATgetArgument(appl, 1)));
      _assert(lindex != NULL);
      Expression *rindex = dynamic_cast<Expression*>(parseATerm(ATgetArgument(appl, 2)));
      _assert(rindex != NULL);

      return new XTCArrayIndex(&Unknown, data, lindex, rindex);
    } 

    if (!strcmp(funname, "StringLit"))
    {
      char *str = 
	ATgetName(ATgetAFun((ATermAppl)ATgetFirst((ATermList)ATgetArgument(appl, 0))));

      ArrayValue *arr = new ArrayValue(new PointerType(&BasicUChar, strlen(str)-1));

      for (unsigned int ii=1; ii != strlen(str)-1; ++ii)
      {
	IntegerValue **ival = new IntegerValue*(new IntegerValue(str[ii]));
	(*ival)->type = &BasicUChar;

	arr->data.push_back((Value**)ival);
      }

      IntegerValue **ival = new IntegerValue*(new IntegerValue(0));
      (*ival)->type = &BasicUChar;
      arr->data.push_back((Value**)ival);

      return arr;
    }

    if (!strcmp(funname, "ArrayInit"))
    {
      /* All elements should be the same type */
      ArrayValue *arr = new ArrayValue();

      ATermList list = (ATermList) ATgetArgument(appl, 0);

      while (!ATisEmpty(list))
      {
	ATerm elem = ATgetFirst(list);

	Value *val = dynamic_cast<Value*>(parseATerm(elem));

	_assert(val != NULL);

	arr->data.push_back(new Value*(val));

	list = ATgetNext(list);
      }

      std::vector<IntegerValue*> bounds;
      bounds.push_back(new IntegerValue(-1));

      /* *** MEMORY LEAK *** */
      if (arr->data.size())
	arr->type = constructType((*arr->data[0])->type, bounds, NULL);
      else
	arr->type = constructType(&BasicInteger, bounds, NULL);

      arr->known = true;

      delete bounds[0];
      
      return arr;
    }

    if (!strcmp(funname, "Positive"))
    {
      Expression *data =
	dynamic_cast<Expression*>(parseATerm(ATgetArgument(appl, 0))); 

      _assert(data != NULL);

      return new Positive(&Unknown, data);
    }

    if (!strcmp(funname, "Negative"))
    {
      Expression *data =
	dynamic_cast<Expression*>(parseATerm(ATgetArgument(appl, 0))); 
      _assert(data != NULL);

      return new Negative(&Unknown, data);
    }

    if (!strcmp(funname, "Negation"))
    {
      Expression *data =
	dynamic_cast<Expression*>(parseATerm(ATgetArgument(appl, 0))); 
      _assert(data != NULL);

      return new Negation(&Unknown, data);
    }

    if (!strcmp(funname, "Tilde"))
    {
      Expression *data =
	dynamic_cast<Expression*>(parseATerm(ATgetArgument(appl, 0))); 
      _assert(data != NULL);

      return new Tilde(&Unknown, data);
    }

    if (!strcmp(funname, "PreIncr"))
    {
      LeftExpression *data =
	dynamic_cast<LeftExpression*>(parseATerm(ATgetArgument(appl, 0))); 
      _assert(data != NULL);

      return new PreIncr(&Unknown, data);
    }

    if (!strcmp(funname, "PostIncr"))
    {
      LeftExpression *data =
	dynamic_cast<LeftExpression*>(parseATerm(ATgetArgument(appl, 0))); 

      return new PostIncr(&Unknown, data);
    }

    if (!strcmp(funname, "PreDecr"))
    {
      LeftExpression *data =
	dynamic_cast<LeftExpression*>(parseATerm(ATgetArgument(appl, 0))); 
      _assert(data != NULL);

      return new PreDecr(&Unknown, data);
    }

    if (!strcmp(funname, "PostDecr"))
    {
      LeftExpression *data =
	dynamic_cast<LeftExpression*>(parseATerm(ATgetArgument(appl, 0))); 

      _assert(data != NULL);
      return new PostDecr(&Unknown, data);
    } 

    if (!strcmp(funname, "Comma"))
    {
      Expression *left =
	dynamic_cast<Expression*>(parseATerm(ATgetArgument(appl, 0))); 
      _assert(left != NULL);

      Expression *right =
	dynamic_cast<Expression*>(parseATerm(ATgetArgument(appl, 1)));
      _assert(right != NULL);

      return new Comma(&Unknown, left, right);
    } 

    if (!strcmp(funname, "Lt") || !strcmp(funname, "Gt") ||
	!strcmp(funname, "Le") || !strcmp(funname, "Ge") ||
	!strcmp(funname, "Add") || !strcmp(funname, "Subt") ||
	!strcmp(funname, "Mul") || !strcmp(funname, "Div") ||
	!strcmp(funname, "Mod") || 
	!strcmp(funname, "Equal") || !strcmp(funname, "NotEqual") ||
	!strcmp(funname, "LOr") || !strcmp(funname, "LAnd") ||
	!strcmp(funname, "IncOr") || !strcmp(funname, "ExOr") ||
	!strcmp(funname, "ShiftLeft") || !strcmp(funname, "ShiftRight") ||
	!strcmp(funname, "And"))
    {
      Expression *left =
	dynamic_cast<Expression*>(parseATerm(ATgetArgument(appl, 0)));
      Expression *right =
	dynamic_cast<Expression*>(parseATerm(ATgetArgument(appl, 1))); 
      
      _assert(left != NULL);
      _assert(right != NULL);

      if (!strcmp(funname, "Lt"))
	return new Lt(&Unknown, left, right);
      if (!strcmp(funname, "Gt"))
	return new Gt(&Unknown, left, right);
      if (!strcmp(funname, "Le"))
	return new Le(&Unknown, left, right);
      if (!strcmp(funname, "Ge"))
	return new Ge(&Unknown, left, right);
      if (!strcmp(funname, "Equal"))
	return new Equal(&Unknown, left, right);
      if (!strcmp(funname, "NotEqual"))
	return new NotEqual(&Unknown, left, right);
      if (!strcmp(funname, "Add"))
	return new Add(&Unknown, left, right);
      if (!strcmp(funname, "Subt"))
	return new Subt(&Unknown, left, right);
      if (!strcmp(funname, "Mul"))
	return new Mul(&Unknown, left, right);
      if (!strcmp(funname, "Div"))
	return new Div(&Unknown, left, right);
      if (!strcmp(funname, "Mod"))
	return new Mod(&Unknown, left, right);
      if (!strcmp(funname, "LOr"))
	return new LOr(&Unknown, left, right);
      if (!strcmp(funname, "LAnd"))
	return new LAnd(&Unknown, left, right);
      if (!strcmp(funname, "And"))
	return new And(&Unknown, left, right);
      if (!strcmp(funname, "IncOr"))
	return new IncOr(&Unknown, left, right);
      if (!strcmp(funname, "ExOr"))
	return new ExOr(&Unknown, left, right);
      if (!strcmp(funname, "ShiftLeft"))
	return new ShiftLeft(&Unknown, left, right);
      if (!strcmp(funname, "ShiftRight"))
	return new ShiftRight(&Unknown, left, right);
    }

    if (!strcmp(funname, "TranslationUnit"))
    {
      TranslationUnit *unit = new TranslationUnit();

      ATermList list = (ATermList) ATgetArgument(appl, 0);

      while (!ATisEmpty(list))
      {
	ATerm elem = ATgetFirst(list);

	unit->push_back(parseATerm(elem));

	list = ATgetNext(list);

      }

      return unit;
    }

    if (!strcmp(funname, "Compound"))
    {
      Compound *block = new Compound();

      ATermList list = (ATermList) ATgetArgument(appl, 0);

      while (!ATisEmpty(list))
      {
	ATerm elem = ATgetFirst(list);

	block->push_back_decl(parseATerm(elem));

	list = ATgetNext(list);
      }
      
      list = (ATermList) ATgetArgument(appl, 1);

      while (!ATisEmpty(list))
      {
	ATerm elem = ATgetFirst(list);

	/* Compounds within compounds are automatic */
	Code *res = parseATerm(elem);
	Compound *cres = dynamic_cast<Compound*>(res);

	if (cres)
	  cres->automatic = true;

	block->push_back_stat(res);

	list = ATgetNext(list);
      }

      return block;
    } 
    
    if (!strcmp(funname, "If"))
    {
      return new If(dynamic_cast<Expression*>(parseATerm(ATgetArgument(appl, 0))),
		    parseATerm(ATgetArgument(appl, 1)));
    } 

    if (!strcmp(funname, "IfElse"))
    {
      return new IfElse(dynamic_cast<Expression*>(parseATerm(ATgetArgument(appl, 0))),
			parseATerm(ATgetArgument(appl, 1)),
			parseATerm(ATgetArgument(appl, 2)));
    } 

    if (!strcmp(funname, "IfExp"))
    {
      return new IfExp(&Unknown,
		       dynamic_cast<Expression*>(parseATerm(ATgetArgument(appl, 0))),
		       dynamic_cast<Expression*>(parseATerm(ATgetArgument(appl, 1))),
		       dynamic_cast<Expression*>(parseATerm(ATgetArgument(appl, 2))));
    } 

    if (!strcmp(funname, "While"))
    {
      return new While(dynamic_cast<Expression*>(parseATerm(ATgetArgument(appl, 0))),
		       parseATerm(ATgetArgument(appl, 1)));
    } 

    if (!strcmp(funname, "DoWhile"))
    {
      return new DoWhile(parseATerm(ATgetArgument(appl, 0)),
			 dynamic_cast<Expression*>(parseATerm(ATgetArgument(appl, 1))));
    } 

    if (!strcmp(funname, "For"))
    {
      return new For(parseATerm(ATgetArgument(appl, 0)),
		     dynamic_cast<Expression*>(parseATerm(ATgetArgument(appl, 1))),
		     parseATerm(ATgetArgument(appl, 2)),
		     parseATerm(ATgetArgument(appl, 3)));
    } 

    if (!strcmp(funname, "FunCall"))
    {
      FunCall *fcl = new FunCall(&Unknown, parseATerm(ATgetArgument(appl, 0)));
		      
      ATermList list = (ATermList) ATgetArgument(appl, 1);
      
      while (!ATisEmpty(list))
      {
	ATerm elem = ATgetFirst(list);
	
	fcl->push_back(dynamic_cast<Expression*>(parseATerm(elem)));
	
	list = ATgetNext(list);
      }

      return fcl;
    } 
    
    if (!strcmp(funname, "FunCallCode"))
    {
      FunCall *fcl = dynamic_cast<FunCall*>(parseATerm(ATgetArgument(appl, 0)));

      ATermList list = (ATermList) ATgetArgument(appl, 1);
      while (!ATisEmpty(list))
      {
	ATerm elem = ATgetFirst(list);
	
	Compound *cpar = dynamic_cast<Compound*>(parseATerm(elem));
	_assert(cpar != NULL);

	fcl->push_back(new CodeValue(cpar));

	list = ATgetNext(list);
      }

      return fcl;
    } 

    if (!strcmp(funname, "Declaration2"))
    {
      TypeSpec *spec;
      bool isStatic=false, isAuto=false, isPromote=false, isExtern=false;

      if (!strcmp(ATgetName(ATgetAFun((ATermAppl)ATgetArgument(appl, 0))), 
                  "DeclSpec"))
      {
        spec = dynamic_cast<TypeSpec*>(parseATerm(ATgetArgument((ATermAppl)ATgetArgument(appl, 0), 1)));
   
        ATermList list = (ATermList) ATgetArgument((ATermAppl)ATgetArgument(appl, 0), 0);

        while (!ATisEmpty(list))
        {
   	  ATerm elem = ATgetFirst(list);

          if (!strcmp(ATgetName(ATgetAFun((ATermAppl)elem)), "Static"))
            isStatic = true;
          if (!strcmp(ATgetName(ATgetAFun((ATermAppl)elem)), "Auto"))
            isAuto = true;
          if (!strcmp(ATgetName(ATgetAFun((ATermAppl)elem)), "Promote"))
            isPromote = true;
          if (!strcmp(ATgetName(ATgetAFun((ATermAppl)elem)), "Extern"))
            isExtern = true;

	  list = ATgetNext(list);
        }
      }
      else
        spec = dynamic_cast<TypeSpec*>(parseATerm(ATgetArgument(appl, 0)));

      Declaration2* decl = new Declaration2(spec, false, isStatic, isAuto, isPromote, isExtern);

      ATermList list = (ATermList) ATgetArgument(appl, 1);

      while (!ATisEmpty(list))
      {
	ATerm elem = ATgetFirst(list);

	decl->push_back(dynamic_cast<IdDecl*>(parseATerm(elem)));

	list = ATgetNext(list);
      }

      return last = decl;
    } 

    if (!strcmp(funname, "FunDef"))
    {
      TypeSpec *typespec;
      IdDecl *iddecl = dynamic_cast<IdDecl*>(parseATerm(ATgetArgument(appl, 1)));
      Compound *data = dynamic_cast<Compound*>(parseATerm(ATgetArgument(appl, 2)));
      bool isStatic=false, isAuto=false, isPromote=false, isExtern=false;

      if (!strcmp(ATgetName(ATgetAFun((ATermAppl)ATgetArgument(appl, 0))), 
                  "DeclSpec"))
      {
        typespec = dynamic_cast<TypeSpec*>(parseATerm(ATgetArgument((ATermAppl)ATgetArgument(appl, 0), 1)));
   
        ATermList list = (ATermList) ATgetArgument((ATermAppl)ATgetArgument(appl, 0), 0);

        while (!ATisEmpty(list))
        {
   	  ATerm elem = ATgetFirst(list);

          if (!strcmp(ATgetName(ATgetAFun((ATermAppl)elem)), "Static"))
            isStatic = true;
          if (!strcmp(ATgetName(ATgetAFun((ATermAppl)elem)), "Auto"))
            isAuto = true;
          if (!strcmp(ATgetName(ATgetAFun((ATermAppl)elem)), "Promote"))
            isPromote = true;
          if (!strcmp(ATgetName(ATgetAFun((ATermAppl)elem)), "Extern"))
            isExtern = true;

	  list = ATgetNext(list);
        }
      }
      else
        typespec = dynamic_cast<TypeSpec*>(parseATerm(ATgetArgument(appl, 0)));

      _assert(typespec != NULL);
      _assert(iddecl != NULL);
      _assert(data != NULL);

      FunDef *fdef = new FunDef(typespec, iddecl, data, false, isStatic, isAuto, isPromote, isExtern);

      return fdef;
    } 

    if (!strcmp(funname, "StrategyDef"))
    {
      IdDecl *iddecl = dynamic_cast<IdDecl*>(parseATerm(ATgetArgument(appl, 0)));
      Compound *data = dynamic_cast<Compound*>(parseATerm(ATgetArgument(appl, 1)));

      _assert(iddecl != NULL);
      _assert(data != NULL);

      StrategyDef *sdef = new StrategyDef(iddecl, data);

      return sdef;
    }

    /* Convert stratego definition to function declaration, such that it
     * will be called as an external strategy. In the mean time, add it to
     * a file that will be compiled by strc later.
     */
    if (!strcmp(funname, "StrategoDef"))
    {
      TypeSpec *typespec;
      IdDecl *iddecl = dynamic_cast<IdDecl*>(parseATerm(ATgetArgument(appl, 1)));
      bool isStatic=false, isAuto=false, isPromote=false, isExtern=false;

      if (!strcmp(ATgetName(ATgetAFun((ATermAppl)ATgetArgument(appl, 0))), 
                  "DeclSpec"))
      {
        typespec = dynamic_cast<TypeSpec*>(parseATerm(ATgetArgument((ATermAppl)ATgetArgument(appl, 0), 1)));
   
        ATermList list = (ATermList) ATgetArgument((ATermAppl)ATgetArgument(appl, 0), 0);

        while (!ATisEmpty(list))
        {
   	  ATerm elem = ATgetFirst(list);

          if (!strcmp(ATgetName(ATgetAFun((ATermAppl)elem)), "Static"))
            isStatic = true;
          if (!strcmp(ATgetName(ATgetAFun((ATermAppl)elem)), "Auto"))
            isAuto = true;
          if (!strcmp(ATgetName(ATgetAFun((ATermAppl)elem)), "Promote"))
            isPromote = true;
          if (!strcmp(ATgetName(ATgetAFun((ATermAppl)elem)), "Extern"))
            isExtern = true;

	  list = ATgetNext(list);
        }
      }
      else
        typespec = dynamic_cast<TypeSpec*>(parseATerm(ATgetArgument(appl, 0)));

      _assert(typespec != NULL);
      _assert(iddecl != NULL);

      Declaration2* decl = new Declaration2(typespec, false, isStatic, isAuto, isPromote, isExtern);
      decl->push_back(iddecl);

      stratego_def_t def;

      Id *id = dynamic_cast<Id*>(iddecl->id);
      _assert(id != NULL);

      def.name = id->name;
      def.def = (ATermList)ATgetArgument(appl, 2);

      for (IdDecl::PI ii=iddecl->params.begin(); ii != iddecl->params.end(); ++ii)
      {
	Id *id = dynamic_cast<Id*>((*ii)->iddecl->id);
	_assert(id != NULL);

        def.params.push_back(id->name);
      }

      strategoDefs.push_back(def);

      return decl;
    } 

    if (!strcmp(funname, "TypeDef"))
    {
      TypeSpec *typespec = dynamic_cast<TypeSpec*>(parseATerm(ATgetArgument(appl, 0)));
      IdDecl *iddecl = dynamic_cast<IdDecl*>(parseATerm(ATgetArgument(appl, 1)));

      TypeDef *tdef = new TypeDef(typespec, iddecl);

      return tdef;
    }

    if (!strcmp(funname, "ParamDecl"))
    {
      if (!strcmp(ATgetName(ATgetAFun((ATermAppl)ATgetArgument(appl, 1))), "None"))
      {
	return new ParamDecl(dynamic_cast<TypeSpec*>(parseATerm(ATgetArgument(appl, 0))),
			     NULL);
      }
      else
      {
	TypeSpec *typespec = dynamic_cast<TypeSpec*>(parseATerm(ATgetArgument(appl, 0)));
	IdDecl *iddecl =
	  dynamic_cast<IdDecl*>(parseATerm(ATgetArgument((ATermAppl)
							 ATgetArgument(appl, 1), 0)));
	
	return new ParamDecl(typespec, iddecl);
      }
    } 
    
    if (!strcmp(funname, "IdDecl"))
    {
      IdDecl* iddecl =
	new IdDecl(parseATerm(ATgetArgument(appl, 1)));

      /* Pointers */
      ATermList list = (ATermList) ATgetArgument(appl, 0);
      while (!ATisEmpty(list))
      {
        PointerOf *d = (PointerOf*)parseATerm(ATgetFirst(list));
        _assert(d != NULL);
        iddecl->push_back(d);

        list = ATgetNext(list);
      }

      if (!strcmp(ATgetName(ATgetAFun((ATermAppl)ATgetArgument(appl, 2))), "None"))
	return iddecl;

      if (ATgetType(ATgetArgument((ATermAppl) ATgetArgument(appl, 2), 0)) == AT_LIST)
      {
	/* Array bounds declaration */
	ATermList list = (ATermList) ATgetArgument((ATermAppl) ATgetArgument(appl, 2), 0);

	while (!ATisEmpty(list))
	{
	  ATerm elem = ATgetFirst(list);
	 
	  if (!strcmp(ATgetName(ATgetAFun((ATermAppl)elem)), "EmptyArrayDecl"))
	    iddecl->push_back(new IntegerValue(-1));
	  else
	  {
	    IntegerValue *bound = dynamic_cast<IntegerValue*>
	      (parseATerm(ATgetArgument((ATermAppl) elem, 0)));
	    
	    _assert(bound != NULL);
	    
	    iddecl->push_back(bound);
	  }
	  
	  list = ATgetNext(list);
	}	
      }
      else
      {
	/* Parameter list */
	ATermAppl ParamList =
	  (ATermAppl)ATgetArgument((ATermAppl)ATgetArgument(appl, 2), 0);

	ATermList list = (ATermList) ATgetArgument(ParamList, 0);
	
	while (!ATisEmpty(list))
	{
	  ATerm elem = ATgetFirst(list);
	  
	  iddecl->push_back(dynamic_cast<ParamDecl*>(parseATerm(elem)));
	  
	  list = ATgetNext(list);
	}

	if (!strcmp(ATgetName(ATgetAFun(ParamList)), "ParamListVarArgs"))
	{
	  if (!strcmp(ATgetName(ATgetAFun((ATermAppl)ATgetArgument(ParamList, 1))), "Some"))
	  {
	    Id *varargparam =
	      dynamic_cast<Id*>(parseATerm(ATgetArgument((ATermAppl)ATgetArgument(ParamList, 1), 0)));
	    _assert(varargparam != NULL);
	    iddecl->varargparam = varargparam;
	  }
	}
      } 

      return iddecl;
    }

    if (!strcmp(funname, "DeclInit"))
    {
      IdDecl* iddecl = dynamic_cast<IdDecl*>(parseATerm(ATgetArgument(appl, 0)));
      iddecl->init = dynamic_cast<Expression*>(parseATerm(ATgetArgument(appl, 1)));

      return iddecl;
    }

    if (!strcmp(funname, "AssignInit"))
    {
      return dynamic_cast<Expression*>(parseATerm(ATgetArgument(appl, 0)));
    }

    if (!strcmp(funname, "TypeSpec"))
    {
      bool isSigned = true, typeInList = false;
      std::string typeName;

      ATermAppl appl2 = (ATermAppl)ATgetArgument(appl, 1);

      if (!strcmp(ATgetName(ATgetAFun(appl2)), "Unsigned"))
      {
	isSigned = false;
	typeInList = true;
      }
      else if (!strcmp(ATgetName(ATgetAFun(appl2)), "Signed"))
	typeInList = true;

      if (typeInList)
      {
	if (ATgetLength((ATermList)ATgetArgument(appl, 2)))
	  typeName =
	    ATgetName(ATgetAFun((ATermAppl)ATgetFirst((ATermList)ATgetArgument(appl, 2))));
	else
	  typeName = "Int";
      }
      else
	typeName = ATgetName(ATgetAFun(appl2));

      if (typeName == "Void")
	return new TypeSpec(&BasicVoid);
      if (typeName =="Char")
	if (isSigned) return new TypeSpec(&BasicChar);
	else          return new TypeSpec(&BasicUChar);
      if (typeName =="Short")
	if (isSigned) return new TypeSpec(&BasicShort);
	else          return new TypeSpec(&BasicUShort);
      if (typeName =="Long" || typeName =="Int")
	if (isSigned) return new TypeSpec(&BasicInteger);
	else  	      return new TypeSpec(&BasicUInteger);
      if (typeName =="Float")
	return new TypeSpec(&BasicFloat);
      if (typeName =="Double")
	return new TypeSpec(&BasicDouble);

      if (!strcmp(ATgetName(ATgetAFun(appl2)), "Code"))
	return new TypeSpec(&BasicCode);
      
      Code *spec = parseATerm(ATgetArgument(appl, 1));

      return new TypeSpec(&Unknown, spec);
    }

    if (!strcmp(funname, "TypeId"))
    {
      return new TypeId(&Unknown, name);
    }
     
    if (!strcmp(funname, "TypeDecode"))
    {
      Expression *exp = dynamic_cast<Expression*>(parseATerm(ATgetArgument(appl, 0)));
      _assert(exp != NULL);

      return new TypeDecode(&Unknown, exp);
    }
     
    if (!strcmp(funname, "PointerOf"))
    {
      PointerOf *res = new PointerOf();

      ATermList list = (ATermList) ATgetArgument(appl, 0);

      while (!ATisEmpty(list))
      {
        char *elname = ATgetName(ATgetAFun((ATermAppl)ATgetFirst(list)));

        if (!strcmp(elname, "Const")) res->isConst = true;
        if (!strcmp(elname, "Volatile")) res->isVolatile = true;
        if (!strcmp(elname, "Restrict")) res->isRestrict = true;

	list = ATgetNext(list);
      }
   
      return res;
    }
 
    if (!strcmp(funname, "TypeName"))
    {
      TypeSpec *typespec = dynamic_cast<TypeSpec*>(parseATerm(ATgetArgument(appl, 0)));
      _assert(typespec != NULL);

      TypeName *tname = new TypeName(typespec);
      ATermList bounds = NULL;

      if (!strcmp(ATgetName(ATgetAFun((ATermAppl)ATgetArgument(appl, 1))), "Some"))
      {
	if (ATgetType(ATgetArgument((ATermAppl)ATgetArgument(appl, 1), 0)) != AT_LIST)
	{
	  ATermList list = (ATermList) ATgetArgument((ATermAppl) ATgetArgument((ATermAppl) ATgetArgument(appl, 1), 0), 0);

	  while (!ATisEmpty(list))
	  {
	    PointerOf *d = dynamic_cast<PointerOf*>(parseATerm(ATgetFirst(list)));
	    _assert(d != NULL);
	    tname->push_back(d);
	    
	    list = ATgetNext(list);
	  }

	  if (!strcmp(ATgetName(ATgetAFun((ATermAppl) ATgetArgument((ATermAppl) ATgetArgument((ATermAppl)ATgetArgument(appl, 1), 0), 1))), "Some"))
	    bounds = (ATermList) ATgetArgument((ATermAppl) ATgetArgument((ATermAppl) ATgetArgument((ATermAppl)ATgetArgument(appl, 1), 0), 1), 0);
	}
	else
	  bounds = (ATermList) ATgetArgument((ATermAppl)ATgetArgument(appl, 1), 0);

	if (bounds)
	{
	  while (!ATisEmpty(bounds))
	  {
	    ATerm elem = ATgetFirst(bounds);

	    if (!strcmp(ATgetName(ATgetAFun((ATermAppl)elem)), "EmptyArrayDecl"))
	      tname->push_back(new IntegerValue(-1));
	    else
	    {
	      IntegerValue *bound = dynamic_cast<IntegerValue*>
		(parseATerm(ATgetArgument((ATermAppl) elem, 0)));
	      
	      _assert(elem != NULL);
	      
	      tname->push_back(bound);
	    }
	    
	    bounds = ATgetNext(bounds);
	  }
	}
      }

      return tname;
    }

    if (!strcmp(funname, "Struct"))
    {
      StructSpec *str = new StructSpec();

      ATermList list = (ATermList) ATgetArgument(appl, 1);

      while (!ATisEmpty(list))
      {
	/* Member declaration (type) */
	ATermAppl elem = (ATermAppl) ATgetFirst(list);
	
	TypeSpec *typespec = dynamic_cast<TypeSpec*>(parseATerm(ATgetArgument(elem, 0)));

	ATermList list2 = (ATermList) ATgetArgument(elem, 1);

	while (!ATisEmpty(list2))
	{
	  /* Member declaration (id) */
	  ATerm elem2 = ATgetFirst(list2);
	    
	  IdDecl* iddecl = dynamic_cast<IdDecl*>(parseATerm(elem2));
	    
	  str->push_back(new MemDecl(typespec->copy(), iddecl));

	  list2 = ATgetNext(list2);
	}
	
	delete typespec;

	list = ATgetNext(list);
      }
      
      return str;
    }

    if (!strcmp(funname, "Break"))
    {
      return new Break();
    }

    if (!strcmp(funname, "Return"))
    {
      return new Return(&Unknown,
			dynamic_cast<Expression*>(parseATerm(ATgetArgument(appl, 0))));
    }

    if (!strcmp(funname, "PpControl"))
    {      
      return last = new PpControl(name);
    }

    plog.warning() << HDR << "unknown term '" << funname << "'" << std::endl;
  }

  return new VoidValue();
}
